package entidades;

import entidades.interfaces.ITelhaBaseVisitante;
import gestordeentidades.GestorDeEntidades;
import java.util.ArrayList;
import java.util.Objects;
import javafx.beans.property.ObjectProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;

/**
 * Trabalho DAS - 2014/2015
 *
 * @author a21210380, a21190325, a21210392
 */
public class Telha extends TelhaBase {
    public static final float MIN_SIZE = 65f;

    private Familia familia;
    private Rectangulo rectangulo = null;
    private TipoTelha tipoDeTelha = null;
    private Estilo estilo = null;
    private DesenhoDinamico desenho = null;
    protected ObservableList<Ancora> ancoras = null;

    public Telha(Familia familia, Rectangulo rectangulo, TipoTelha tipoDeTelha, Estilo estilo, DesenhoDinamico desenho) {
        this.desenho = desenho;
        this.familia = familia;
        this.rectangulo = rectangulo;
        this.tipoDeTelha = tipoDeTelha;
        this.estilo = estilo;
    }

    public Telha(Telha telha) {
        this.desenho = new DesenhoDinamico(telha.getDesenho());
        this.familia = telha.getFamilia();
        Ponto origem = new Ponto(telha.getRectangulo().getOrigem());
        this.rectangulo = new Rectangulo(origem, telha.getRectangulo().getAltura(), telha.getRectangulo().getLargura(), telha.getRectangulo().getMinSize());
        this.tipoDeTelha = telha.getTipoDeTelha();
        this.estilo = telha.getEstilo();
        ArrayList<Ancora> arrayList = new ArrayList<>(telha.getAncoras().size());
        telha.getAncoras().forEach((Ancora a) -> {
            arrayList.add(new Ancora(new Ponto(a.getPonto()), this));
        });
        this.ancoras = FXCollections.observableList(arrayList);
    }
    
    @Override
    public boolean adicionarAncora(Ancora ancora) {
        if (ancoras == null) {
            ancoras = FXCollections.observableList(
                    GestorDeEntidades.getInstance().filtrarEntidades(Ancora.class, (Ancora e) -> {
                        return e.getTelha() == this;
                    }));
        }
        return ancoras.add(ancora);
    }
    
    @Override
    public ObservableList<Ancora> getAncoras() {
        if (ancoras == null) {
            ancoras = FXCollections.observableList(
                    GestorDeEntidades.getInstance().filtrarEntidades(Ancora.class, (Ancora e) -> {
                        return e.getTelha() == this;
                    }));
        }
        return ancoras;
    }

    @Override
    public boolean removerAncora(Ancora ancora) {
        if (ancoras == null) {
            ancoras = FXCollections.observableList(
                    GestorDeEntidades.getInstance().filtrarEntidades(Ancora.class, (Ancora e) -> {
                        return e.getTelha() == this;
                    }));
        }
        return ancoras.remove(ancora);
    }

    @Override
    public Cor getCorDaLinha() {
        return estilo.getCorDaLinha();
    }

    @Override
    public Cor getCorDoTexto() {
        return estilo.getCorDoTexto();
    }

    @Override
    public DesenhoDinamico getDesenho() {
        return desenho;
    }

    @Override
    public Textura getFundo() {
        return estilo.getFundo();
    }

    @Override
    public void setCorDaLinha(Cor cor) {
        estilo.setCorDaLinha(cor);
    }

    @Override
    public void setCorDoTexto(Cor cor) {
        estilo.setCorDoTexto(cor);
    }

    @Override
    public void setFundo(Textura textura) {
        estilo.setFundo(textura);
    }

    @Override
    public Rectangulo getRectangulo() {
        return rectangulo;
    }

    @Override
    public String getNomeDoEstilo() {
        return estilo.getNomeDoEstilo();
    }

    @Override
    public void setNomeDoEstilo(String nome) {
        estilo.setNomeDoEstilo(nome);
    }

    @Override
    public TipoTelha getTipoDeTelha() {
        return tipoDeTelha;
    }

    @Override
    public TelhaBase setTipoDeTelha(TipoTelha tipoDeTelha) {
        this.tipoDeTelha = tipoDeTelha;
        return this;
    }

    @Override
    public Familia getFamilia() {
        return familia;
    }
    
    @Override
    public ObjectProperty<Cor> corLinhaProperty() {
        return estilo.corLinhaProperty();
    }
    
    @Override
    public ObjectProperty<Cor> corTextoProperty() {
        return estilo.corTextoProperty();
    }
    
    @Override
    public ObjectProperty<Textura> fundoProperty() {
        return estilo.fundoProperty();
    }
    
    @Override
    public void mover(float x, float y) {
        float movX = x - rectangulo.getOrigem().getX();
        float movY = y  - rectangulo.getOrigem().getY();
        
        ancoras.forEach((Ancora c) -> {
            c.getPonto().set(c.getPonto().getX() + movX, c.getPonto().getY() + movY);
            c.getPonto().registarEntidadeModificada();
        });
        
        rectangulo.getOrigem().set(x, y);
        rectangulo.getOrigem().registarEntidadeModificada();
    }
    
    @Override
    public void resize(float x, float y) {
        Ponto origem = rectangulo.getOrigem();
        
        float largura = x - (origem.getX() + rectangulo.getLargura());
        float altura = y - (origem.getY() + rectangulo.getAltura());
        
        float movX = largura;
        float movY = altura;
        
        ancoras.forEach((Ancora c) -> {
            if(c.getPonto().getX() == origem.getX() + rectangulo.getLargura()){
                c.getPonto().set(c.getPonto().getX() + movX, c.getPonto().getY());
            } else if(c.getPonto().getY() == origem.getY() + rectangulo.getAltura()){
                c.getPonto().set(c.getPonto().getX(), c.getPonto().getY() + movY);
            }            
            c.getPonto().registarEntidadeModificada();
        });
        
        rectangulo.setLargura(rectangulo.getLargura() + largura);
        rectangulo.setAltura(rectangulo.getAltura() + altura);
        
        rectangulo.registarEntidadeModificada();
    }

    public Telha setFamilia(Familia familia) {
        this.familia = familia;
        return this;
    }

    public Estilo getEstilo() {
        return estilo;
    }

    public Telha setEstilo(Estilo estilo) {
        this.estilo = estilo;
        return this;
    }
    
    @Override
    public void accept(ITelhaBaseVisitante visitor) {
        visitor.visitante(this);
    }
    
    @Override
    public void acceptChain(ITelhaBaseVisitante visitor) {
        visitor.visitante(this);
    }

    @Override
    public TelhaBase clone() {
        return new Telha(this);
    }

    
    
    @Override
    public boolean equals(Object obj) {
        if (obj == null) {
            return false;
        }
        if (obj == this) {
            return true;
        }
        if (obj instanceof Telha) {
            Telha c = (Telha) obj;
            return estilo.equals(c.getEstilo())
                    && familia.equals(c.getFamilia())
                    && rectangulo.equals(c.getRectangulo())
                    && tipoDeTelha.equals(c.getTipoDeTelha());
        }
        return false;
    }

    @Override
    public int hashCode() {
        int hash = 5;
        hash = 97 * hash + Objects.hashCode(this.familia);
        hash = 97 * hash + Objects.hashCode(this.rectangulo);
        hash = 97 * hash + Objects.hashCode(this.tipoDeTelha);
        hash = 97 * hash + Objects.hashCode(this.estilo);
        return hash;
    }
}
